package analyser.node;

import exception.MibException;
import kit.AnalyserIndexerKit;
import kit.LetterIndexer;
import model.MibNode;
import util.common.CharType;
import util.common.CharTypeChecker;
import util.common.StringUtils;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;

 public abstract class AbstractNodeAnalyser<E extends MibNode> implements INodeAnalyser {
    protected List<MibNode> parentNodeList = new ArrayList<>(5);
    protected E node;
    protected AnalyserIndexerKit kit;
    protected LetterIndexer indexer;
    protected List<String> txtList;

    public AbstractNodeAnalyser(List<String> txtList) {
        this.txtList = txtList;
    }

    @Override
    public MibNode analyse(AnalyserIndexerKit kit){
        if (kit == null) {
            throw new NullPointerException("索引分析包kit不能为null.");
        }
        this.kit = kit;
        LetterIndexer indexer = kit.getIndexer();
        if (indexer == null) {
            throw new NullPointerException("索引器不能为null.");
        }
        this.indexer = indexer;
        indexer.reset();
        if (node == null){
            throw new NullPointerException("AbstractAnalyser的node节点应该在构造器被初始化。");
        }
        node.clazzName = node.getClass().getName();
        node.txt = indexer.getTxt();
        doAnalyse();
        return node;
    }

     protected abstract void doAnalyse();

    /**
     * 提取父节点名字及其当前节点编号
     * txt -> ::= { console 1 }
     * indexer应该指向"{"
     */
    protected void analyseLeaf(){
        if (node == null){
            throw new IllegalArgumentException();
        }
        doAnalyseLeaf();
    }

    private void doAnalyseLeaf(){
        if (node == null){
            throw new IllegalArgumentException();
        }
        int state = 0;
        String str = null;
        String temp = null;
        String parentParentName = null;
        String parentName = null;
        int n = -1;
        while (state != 99){
            switch (state){
                case 0:
                    if ((state = kit.nextMatch("{", 1, 0)) == 1);
                    break;
                case 1:
                    str = indexer.getNextIgnoreLeftSpace(CharType.Hyphen,CharType.Number, CharType.Letter);
                    if (StringUtils.isNotNullOrWhiteSpace(str)){
                        parentName = str;
                        state = 2;
                    }else {
                        state = -state;
                    }
                    break;
                case 2:
                    char c = indexer.probeNextCharIgnoreLeftSpace();
                    if (CharTypeChecker.check(c, CharType.Letter)){ // ::={iso org(3) dod(6) 1 }
                        temp = indexer.getNextIgnoreLeftSpace(CharType.Number, CharType.Hyphen, CharType.Letter);
                        if (StringUtils.isNotNullOrWhiteSpace(temp)){
                            parentParentName = parentName;
                            parentName = temp;
                            state = 3;
                        }else{
                            state = -state;
                        }
                    }else if(CharTypeChecker.check(c, CharType.Number)){ // ::={iso 1 }
                        temp = indexer.getNextIgnoreLeftSpace(CharType.Number);
                        if (StringUtils.isNotNullOrWhiteSpace(temp)){
                            n = Integer.valueOf(temp);
                            state = 7;
                        }else {
                            state = -state;
                        }
                    }else {
                        state = -state;
                    }
                    break;
                case 3:
                    state = kit.nextMatch("(", 4, -state);
                    break;
                case 4:
                    temp = indexer.getNextIgnoreLeftSpace(CharType.Number); // 新的当前父节点名
                    if (StringUtils.isNotNullOrWhiteSpace(temp)){
                        n = Integer.valueOf(temp);
                        createAndAddMibNode(parentName, n, parentParentName);
                        state = 5;
                    }else {
                        state = -state;
                    }
                    break;
                case 5:
                    state = kit.nextMatch(")", 6, -state);
                    break;
                case 6:
                    state = 2;
                    break;
                case 7:
                    if ((state = kit.nextMatch("}", 99, -7)) == 99){
                    }else{
                        temp = indexer.getNextIgnoreLeftSpace(CharType.Number);
                        if (StringUtils.isNotNullOrWhiteSpace(temp)){
                            parentParentName = parentName;
                            parentName = parentName + n;
                            createAndAddMibNode(parentName, n, parentParentName);
                            n = Integer.valueOf(temp);
                        }else{
                            state = -state;
                        }
                    }
                    break;
                default:
                    kit.ThrowAnalysisException("解析叶子部分异常。", state, str);
            }
        }
        node.parentName = parentName;
        node.node = n;
    }

     /**
      * 根据节点名和节点编号生成新的mib节点，并加入父节点列表。
      * 用于一个节点中定义了多个节点的情况。
      * @param name
      * @param node
      * @param parentNodeName
      */
    private void createAndAddMibNode(String name, int node, String parentNodeName){
        MibNode mibNode = new MibNode(name);
        mibNode.node = node;
        mibNode.parentName = parentNodeName;
        mibNode.clazzName = MibNode.class.getName();
        this.parentNodeList.add(mibNode);
    }

    /**
     * 提取格式化文本的编号：#{STRINDEX:2088} -> 2088
     * 要求：索引刚好指在#号上或#号前的空格上。
     * 提取后索引会指在}的下一位。
     * @return
     */
    protected int extractFormatText(){
        if (node == null){
            throw new IllegalArgumentException();
        }
        return doExtractFormatText();
    }

    private int doExtractFormatText(){
        int state = 1;
        indexer.backupIndex();
        String str = null;
        int indexNumber = -1;
        while (state != 99){
            switch (state){
                case 1: // #{STRINDEX:" + txtList.size() + "}
                    state = kit.nextMatch("#{STRINDEX:", 2, -state);
                    break;
                case 2:
                    str = indexer.getNextIgnoreLeftSpace(CharType.Number);
                    if (StringUtils.isNotNullOrWhiteSpace(str)){
                        indexNumber = Integer.valueOf(str);
                        state = 3;
                    }else{
                        state = -state;
                    }
                    break;
                case 3:
                    state = kit.nextMatch("}", 99, -state);
                    break;
                default:
                    kit.ThrowAnalysisException("解析格式化文本异常", state, str);
            }
        }
        return indexNumber;
    }

    /**
     * 解析格式化文本
     * @return
     */
    protected String extractAndAnalyseFormatText(){
        if (txtList == null){
            throw new IllegalArgumentException();
        }
        int index = extractFormatText();
        if (index >= 0 && index < txtList.size()){
            return txtList.get(index);
        }else if (index < 0){
            throw new MibException("提取格式化索引失败。indexTypes=" + index);
        }else{
            throw new MibException("提取格式化索引异常。indexTypes=" + index);
        }
    }

     @Override
     public List<MibNode> getParentNodes() {
         return this.parentNodeList;
     }


     /**
      * 自动匹配键与对应的值
      * @param matcher 键的值
      * @param fieldName 对应的节点属性,属性必须是public
      * @param essential 该键值是否必须存在，在根据给定规则读取不到对应期待的键时：
      *                  该值为true时，则视为当前节点解析出现语法错误，返回false
      *                  该值为false时，则视为当前节点没有定义该键值，返回true
      * @param isFormatText  是否是格式化的字符串，若是则charTypes失效，否则根据charTypes读取字符串。
      * @param charTypes 对应值的字符类型
      * @return
      */
     public boolean matchKeyValue(String matcher, String fieldName, boolean essential, boolean isFormatText,CharType ... charTypes){
         if (matcher == null)
             throw new IllegalArgumentException("matcher不能为空。");
         boolean status = false;
        if (kit.getIndexer().isNextMatchIgnoreLeftSpace(matcher)){
            String str;
            if (isFormatText){
                str = extractAndAnalyseFormatText(); // 格式化字符串
            }else{
                str = kit.getIndexer().getNextIgnoreLeftSpace(charTypes);  // 匹配对应类型字符串
            }
            if (StringUtils.isNotNullOrWhiteSpace(str)){
                status = setValue(fieldName, str);
            }
         }else if (!essential){
            status = true;
        }
        return status;
     }

     /**
      * 自动匹配键与值，匹配的字段通过matcher自动生成
      * @return
      */
     public boolean matchKeyValue(String matcher, boolean essential, boolean isFormatText, CharType ... charTypes){
         if (matcher == null)
             throw new IllegalArgumentException("matcher不能为空。");
         String field = transferMatcherToField(matcher);
         return matchKeyValue(matcher, field, essential, isFormatText, charTypes);
     }

     /**
      * 自动匹配可省略的键值对，匹配的字段通过matcher自动生成
      * @return
      */
     public boolean matchOmissibleKeyValue(String matcher, boolean isFormatText, CharType ... charTypes){
         if (matcher == null)
             throw new IllegalArgumentException("matcher不能为空。");
         String field = transferMatcherToField(matcher);
         return matchKeyValue(matcher, field, false, isFormatText, charTypes);
     }

     /**
      * 自动匹配键与值，匹配的字段通过matcher自动生成
      * @return
      */
     public boolean matchEssentialKeyValue(String matcher, boolean isFormatText, CharType ... charTypes){
         if (matcher == null)
             throw new IllegalArgumentException("matcher不能为空。");
         String field = transferMatcherToField(matcher);
         return matchKeyValue(matcher, field, true, isFormatText, charTypes);
     }

     /**
      * 通过marcher生成字段名。
      * 生成规则：matcher：MAX-ACCESS => maxAccess
      * @param matcher mib库键名
      * @return
      */
     private String transferMatcherToField(String matcher){
         char[] chars = matcher.toLowerCase().toCharArray();
         boolean flag = false;
         StringBuilder fieldNameBuilder = new StringBuilder();
         // matcher:MAX-ACCESS => field:maxAccess
         for (int i = 0; i < chars.length; i++) {
             char c = chars[i];
             if (c == '-' ||  c == '_'){
                 flag = true;
             }else if (flag){
                 flag = false;
                 fieldNameBuilder.append((char)(c - 32)); // 转大写
             }else {
                 fieldNameBuilder.append(c);
             }
         }
         return fieldNameBuilder.toString();
     }

     /**
      * 根据给定的字符类型匹配出对应的字符串，若字符串不为空，则赋值给指定字段。
      * @param fieldName 字段名
      * @param charTypes 字符类型
      * @return
      */
     public boolean matchNotNullValue(String fieldName, CharType ... charTypes){
         boolean status = false;
         String str = indexer.getNextIgnoreLeftSpace(charTypes);
         if (StringUtils.isNotNullOrWhiteSpace(str)){
             status = setValue(fieldName, str);
         }
         return status;
     }

     /**
      * 反射赋值。
      * @param fieldName
      * @param value
      * @return
      */
     private boolean setValue(String fieldName, Object value){
         boolean status = false;
         try {
             Field field = this.node.getClass().getField(fieldName);
             field.set(this.node, value);
             status = true;
         } catch (NoSuchFieldException e) {
             e.printStackTrace();
         } catch (IllegalAccessException e) {
             e.printStackTrace();
         }
         return status;
     }
 }
